import {
    PlayGroundWidth,
    PlayGroundHeight,
} from "./canvas.js";
import { TikTok, deepCopy, caculateWidthHeight, tankStore } from "./utils.js";
import { GameMap } from "./map.js";
import { Bullet } from "./bullet.js";

// 基本时间单位 tik
// 基本空间单位 block
// 坐标值coord基本单位 block
const TypeAttr = {
    normalPlayer: {
        speed: 5,           // 每speed个tik移动1个block
        blood: 1,
        shape: (() => {
            let outline = [
                [4, 0], [5, 0], [5, 4], [8, 4], [8, 2], [9, 2],
                [9, 9], [8, 9], [8, 8], [6, 8], [5, 9], [4, 9], [3, 8], [1, 8], [1, 9], [0, 9],
                [0, 2], [1, 2], [1, 4], [4, 4],
            ];
            outline.opt = {
                fillColor: "#efa956",
            };
            let inline = [
                [4, 0], [5, 0], [5, 5], [7, 5], [7, 7], [2, 7], [2, 5], [4, 5],
            ];
            inline.opt = {
                fillColor: "#b15f0b",
            };
            let wheels = [
                [[0, 3], [1, 3]],
                [[0, 5], [1, 5]],
                [[0, 7], [1, 7]],
                [[0, 8], [1, 8]],
                [[8, 3], [9, 3]],
                [[8, 5], [9, 5]],
                [[8, 7], [9, 7]],
                [[8, 9], [9, 9]],
            ];
            wheels.forEach(w => {
                w.opt = {
                    strokeColor: "#866130",
                    lineWidth: 1,
                };
            });
            return [outline, inline, ...wheels];
        })(),
        gunBias: { x: 4, y: 0 },
        fireSpeed: 150,     // 每fireSpeed个tik打1发子弹
        firePower: 1,
        fireDirection: "up",
    },
    strongerPlayer: {
        speed: 10,           // 每speed个tik移动1个block
        blood: 2,
        shape: (() => {
            let outline = [
                [4, 0], [5, 0], [5, 2], [7, 2], [8, 1], [9, 1],
                [9, 8], [8, 9], [1, 9], [0, 8],
                [0, 1], [1, 1], [2, 2], [4, 2],
            ];
            outline.opt = {
                fillColor: "#efa956",
            };
            let inline = [
                [4, 0], [5, 0], [5, 3], [7, 3],
                [7, 8], [2, 8], [2, 3], [4, 3],
            ];
            inline.opt = {
                fillColor: "#866130",
            };
            let inline1 = [
                [3, 4], [6, 4], [6, 7], [3, 7],
            ];
            inline1.opt = {
                fillColor: "#b15f0b",
            };
            let wheels = [
                [[0, 2], [1, 2]],
                [[0, 4], [1, 4]],
                [[0, 6], [1, 6]],
                [[0, 8], [1, 8]],
                [[8, 2], [9, 2]],
                [[8, 4], [9, 4]],
                [[8, 6], [9, 6]],
                [[8, 8], [9, 8]],
            ];
            wheels.forEach(w => {
                w.opt = {
                    strokeColor: "#866130",
                    lineWidth: 1,
                };
            });
            return [outline, inline, inline1, ...wheels];
        })(),
        gunBias: { x: 4, y: 0 },
        fireSpeed: 150,     // 每fireSpeed个tik打1发子弹
        firePower: 2,
        fireDirection: "up",
    },
    beeRobot: {
        speed: 8,           // 每speed个tik移动1个block
        blood: 1,
        shape: (() => {
            let outline = [
                [4, 0], [5, 0], [5, 1], [7, 1], [8, 2],
                [9, 2], [9, 3], [8, 3], [8, 4], [9, 4],
                [9, 6], [8, 6], [8, 7], [9, 7], [9, 8], [8, 8],
                [7, 9], [2, 9], [1, 8], [0, 8],
                [0, 7], [1, 7], [1, 6], [0, 6], [0, 4], [1, 4], [1, 3], [0, 3],
                [0, 2], [1, 2], [2, 1], [4, 1],
            ];
            outline.opt = {
                fillColor: "#efefef",
            };
            let inline = [
                [4, 0], [5, 0], [5, 3], [6, 3], [7, 4], [7, 8], [2, 8], [2, 4], [3, 3], [4, 3],
            ];
            inline.opt = {
                strokeColor: "#9f9f9f",
            };
            let inline2 = [
                [4, 4], [5, 4], [6, 5], [6, 6], [5, 7], [4, 7], [3, 6], [3, 5],
            ];
            inline2.opt = {
                fillColor: "#9f9f9f",
            };
            return [outline, inline, inline2];
        })(),
        gunBias: { x: 4, y: 0 },
        fireSpeed: 150,     // 每fireSpeed个tik打1发子弹
        firePower: 1,
        fireDirection: "down",
    },
    normalRobot: {
        speed: 10,           // 每speed个tik移动1个block
        blood: 1,
        shape: (() => {
            let outline = [
                [4, 0], [5, 0], [5, 4], [8, 4], [8, 2], [9, 2],
                [9, 9], [8, 9], [8, 8], [6, 8], [5, 9], [4, 9], [3, 8], [1, 8], [1, 9], [0, 9],
                [0, 2], [1, 2], [1, 4], [4, 4],
            ];
            outline.opt = {
                fillColor: "#efefef",
            };
            let inline = [
                [4, 0], [5, 0], [5, 5], [7, 5], [7, 7], [2, 7], [2, 5], [4, 5],
            ];
            inline.opt = {
                fillColor: "#9f9f9f",
            };
            let wheels = [
                [[0, 3], [1, 3]],
                [[0, 5], [1, 5]],
                [[0, 7], [1, 7]],
                [[0, 8], [1, 8]],
                [[8, 3], [9, 3]],
                [[8, 5], [9, 5]],
                [[8, 7], [9, 7]],
                [[8, 9], [9, 9]],
            ];
            wheels.forEach(w => {
                w.opt = {
                    strokeColor: "#9f9f9f",
                    lineWidth: 1,
                };
            });
            return [outline, inline, ...wheels];
        })(),
        gunBias: { x: 4, y: 0 },
        fireSpeed: 150,     // 每fireSpeed个tik打1发子弹
        firePower: 1,
        fireDirection: "down",
    },
    strongerRobot: {
        speed: 10,           // 每speed个tik移动1个block
        blood: 2,
        shape: (() => {
            let outline = [
                [4, 0], [5, 0], [5, 2], [7, 2], [8, 1], [9, 1],
                [9, 8], [8, 9], [1, 9], [0, 8],
                [0, 1], [1, 1], [2, 2], [4, 2],
            ];
            outline.opt = {
                fillColor: "#efefef",
            };
            let inline = [
                [4, 0], [5, 0], [5, 3], [7, 3],
                [7, 8], [2, 8], [2, 3], [4, 3],
            ];
            inline.opt = {
                fillColor: "#9f9f9f",
            };
            let inline1 = [
                [3, 4], [6, 4], [6, 7], [3, 7],
            ];
            inline1.opt = {
                fillColor: "#9f9f9f",
            };
            let wheels = [
                [[0, 2], [1, 2]],
                [[0, 4], [1, 4]],
                [[0, 6], [1, 6]],
                [[0, 8], [1, 8]],
                [[8, 2], [9, 2]],
                [[8, 4], [9, 4]],
                [[8, 6], [9, 6]],
                [[8, 8], [9, 8]],
            ];
            wheels.forEach(w => {
                w.opt = {
                    strokeColor: "#9f9f9f",
                    lineWidth: 1,
                };
            });
            return [outline, inline, inline1, ...wheels];
        })(),
        gunBias: { x: 4, y: 0 },
        fireSpeed: 150,     // 每fireSpeed个tik打1发子弹
        firePower: 2,
        fireDirection: "down",
    },
};

const DefaultDirection = "up";

export class Tank {
    constructor (opt = {}) {
        const {
            parent,
            name,
            type,                   // "beeRobot", "normalRobot", "strongerRobot"
            defaultDirection = DefaultDirection,
            coord,
            beShotCb = () => {},
            canNotMove = () => {},
        } = opt;
        this._optBackUp = deepCopy(opt);
        this.parent = parent;
        this.name = name;               // type name
        this.type = type;               // tank type: TypeArray中的key
        this.attr = {                   // tank各种属性
            ...deepCopy(TypeAttr[type]),
            ...caculateWidthHeight(TypeAttr[type].shape),
            ...transformShape(TypeAttr[type].shape, TypeAttr[type].gunBias, defaultDirection),
        };
        this.coord = coord;              // tank当前坐标
        this.beShotCb = beShotCb;
        this.canNotMove = canNotMove;

        this.canBeHitByBullet = true;   // 能被bullet击中
        this.canBeHitByPlayer = true;   // 能被tank撞上
        this.canBeHitByRobot = true;    // 能被robot撞上
        this._directionStack = [];      // 方向指令栈，栈顶的方向就是当前的方向，栈为空时说明没有移动
        this._direction = "";           // "", "left", "right", "up", "down"
        this._firingDirection = this.attr.fireDirection;     // 开火向上, 默认向上

        this._firing = false;           // true 正在开火
        this._bulletStore = [];         // 打出去的子弹

        this._fireFn = () => {
            this._bulletStore.push(new Bullet({
                parent: this,
                power: this.attr.firePower,
                direction: this._firingDirection,
                coord: {
                    x: this.coord.x + this.attr.gunBias.x,
                    y: this.coord.y + this.attr.gunBias.y,
                },
            }));
        };
        this._refreshStatus = () => {
            if (!this.isHitEdge() && !this.isHitTankOrBrick()) {
                if (this._direction === "up") {
                    this.coord.y -= 1;
                } else if (this._direction === "down") {
                    this.coord.y += 1;
                } else if (this._direction === "left") {
                    this.coord.x -= 1;
                } else if (this._direction === "right") {
                    this.coord.x += 1;
                }
            } else {
                // 无法移动
                this.canNotMove();
            }
        };

        // 每this.attr.speed个tik刷新一次tank状态
        TikTok.register(this._refreshStatus, this.attr.speed);
    }

    getType() {
        return "tank";
    }

    reset() {
        this.type = this._optBackUp.type;
        this.attr = {                   // tank各种属性
            ...deepCopy(TypeAttr[this._optBackUp.type]),
            ...caculateWidthHeight(TypeAttr[this._optBackUp.type].shape),
            ...transformShape(
                TypeAttr[this._optBackUp.type].shape,
                TypeAttr[this._optBackUp.type].gunBias,
                this._optBackUp.defaultDirection || DefaultDirection),
        };
        this.coord = { ...this._optBackUp.coord };
        this.canBeHitByBullet = true;
        this.canBeHitByPlayer = true;
        this.canBeHitByRobot = true;
        this._directionStack.length = 0;
        this._direction = "";
        this._firingDirection = this.attr.fireDirection;
        this._firing = false;
        this._bulletStore.length = 0;
        TikTok.register(this._refreshStatus, this.attr.speed);
    }

    // 是否撞到playground边缘
    isHitEdge() {
        let res = false;
        if (this._direction === "up") {
            res = this.coord.y - 1 < 0;
        } else if (this._direction === "down") {
            res = this.coord.y + 1 + this.attr.shapeHeight > PlayGroundHeight;
        } else if (this._direction === "left") {
            res = this.coord.x - 1 < 0;
        } else if (this._direction === "right") {
            res = this.coord.x + 1 + this.attr.shapeHeight > PlayGroundWidth;
        }
        return res;
    }

    // 是否击中tank或砖头
    isHitTankOrBrick() {
        // 计算自己的x/y极值
        let { shapeWidth, shapeHeight } = caculateWidthHeight(this.attr.shape);
        let xMin = this.coord.x;
        let xMax = xMin + shapeWidth;
        let yMin = this.coord.y;
        let yMax = yMin + shapeHeight;
        if (this._direction === "up") {
            yMin--;
            yMax--;
        } else if (this._direction === "down") {
            yMin++;
            yMax++;
        } else if (this._direction === "left") {
            xMin--;
            xMax--;
        } else if (this._direction === "right") {
            xMin++;
            xMax++;
        }
        // 是否撞到其它tank
        for (let i = 0; i < tankStore.length; i++) {
            let _tank = tankStore[i];
            if (_tank !== this) {
                // 计算目标tank的x/y的min/max值
                let { shapeWidth: _shapeWidth, shapeHeight: _shapeHeight } = caculateWidthHeight(_tank.attr.shape);
                let _xMin = _tank.coord.x;
                let _xMax = _xMin + _shapeWidth;
                let _yMin = _tank.coord.y;
                let _yMax = _yMin + _shapeHeight;
                if (xMin < _xMax && xMax > _xMin && yMin < _yMax && yMax > _yMin) {
                    if ((_tank.canBeHitByPlayer && this.parent.getType() === "player") || (_tank.canBeHitByRobot && this.parent.getType() === "robot")) {
                        return true;
                    }
                }
            }
        }
        // 是否撞到砖头
        for (let i = 0; i < GameMap.getCurrentMap()._brickStore.length; i++) {
            let brick = GameMap.getCurrentMap()._brickStore[i];
            let _xMin = brick.coord.x;
            let _xMax = _xMin + brick.shapeWidth;
            let _yMin = brick.coord.y;
            let _yMax = _yMin + brick.shapeHeight;
            if (xMin < _xMax && xMax > _xMin && yMin < _yMax && yMax > _yMin) {
                if ((brick.canBeHitByPlayer && this.parent.getType() === "player") || (brick.canBeHitByRobot && this.parent.getType() === "robot")) {
                    brick.beShot(this);
                    return true;
                }
            }
        }
        return false;
    }

    // 切换type
    switchType(type) {
        this.type = type;
        this.attr = {                   // tank各种属性
            ...deepCopy(TypeAttr[type]),
            ...caculateWidthHeight(TypeAttr[type].shape),
            ...transformShape(TypeAttr[type].shape, TypeAttr[type].gunBias, this._firingDirection),
        };
    }

    // 响应键盘操作事件
    onEvent(action = "keyup", d = "") {
        // 操作开火键
        if (d === "fire") {
            this.fire(action === "keydown");
            return;
        }

        // 操作方向键
        let _changed = false;
        if (action === "keyup") {
            // stop action
            let _index = this._directionStack.indexOf(d);
            this._directionStack.splice(_index, 1);
            _changed = true;
        } else {
            // start action
            if (this._directionStack.indexOf(d) === -1) {
                this._directionStack.push(d);
                _changed = true;
            }
        }
        if (_changed) {
            if (this._directionStack.length) {
                this._direction = this._directionStack[this._directionStack.length - 1];
                this._firingDirection = this._direction;
                let _res = transformShape(
                    TypeAttr[this.type].shape,
                    TypeAttr[this.type].gunBias,
                    this._direction,
                );
                this.attr.shape = _res.shape;
                this.attr.gunBias = _res.gunBias;
            } else {
                this._direction = "";
            }
        }
    }

    // 开火
    fire(state) {
        if (state === this._firing) {
            return;
        }

        if (state) {
            this._fireFn();         // 先打一发再进定时器
            this._firing = true;
            TikTok.register(this._fireFn, this.attr.fireSpeed);
        } else {
            this._firing = false;
            TikTok.unRegister(this._fireFn);
        }
    }

    // 被击中
    beShot(byTarget) {
        this.beShotCb(byTarget);
    }

    removeBullet(target) {
        let _index = this._bulletStore.findIndex(item => item === target);
        this._bulletStore.splice(_index, 1);
    }

    destroy() {
        TikTok.unRegister(this._refreshStatus);
        this.attr.shape = [[
            [3, 0], [4, 2], [5, 0], [5, 2], [7, 0], [7, 2],
            [9, 1], [7, 4], [9, 3], [8, 4], [9, 5], [7, 6],
            [9, 8], [6, 7], [6, 9], [4, 7], [2, 9], [2, 7],
            [0, 8], [1, 6], [0, 5], [2, 4], [0, 3], [2, 2],
        ]];
        this.attr.shape[0].opt = {
            fillColor: "#efefef",
        };
        this.canBeHitByBullet = false;
        this.canBeHitByPlayer = false;
        return new Promise(resolve => {
            setTimeout(() => {
                let _index = tankStore.findIndex(tank => tank === this);
                tankStore.splice(_index, 1);
                resolve();
            }, 400);
        });
    }
}

function transformShape(shape, gunBias, direction) {
    // 找到最大的x/y和最小的x/y，方便后面进行y坐标翻转
    let { shapeHeight } = caculateWidthHeight(shape);

    let _shape, _gunBias;
    if (direction === "up") {
        _shape = deepCopy(shape);
        _gunBias = deepCopy(gunBias);
    } else if (direction === "left") {
        // 顺时针旋转后，向x平移，再翻转，可以保证y坐标跟right模式一样
        // 直接逆时针计算，会导致y坐标算出来不一样，因为存在上下镜像翻转，隔了炮管宽度
        _shape = shape.map(graph => {
            let _arr = graph.map(item => [item[1], item[0]]);
            _arr.opt = graph.opt;
            return _arr;
        });
        _gunBias = {
            x: gunBias.y,
            y: gunBias.x,
        };
    } else if (direction === "right") {
        // 顺时针旋转后向x平移
        _shape = shape.map(graph => {
            let _arr = graph.map(item => [shapeHeight - item[1], item[0]]);
            _arr.opt = graph.opt;
            return _arr;
        });
        _gunBias = {
            x: shapeHeight - gunBias.y,
            y: gunBias.x,
        };
    } else if (direction === "down") {
        // y坐标翻转
        _shape = shape.map(graph => {
            let _arr = graph.map(item => [item[0], shapeHeight - item[1]]);
            _arr.opt = graph.opt;
            return _arr;
        });
        _gunBias = {
            x: gunBias.x,
            y: shapeHeight - gunBias.y,
        };
    }
    return {
        shape: _shape,
        gunBias: _gunBias,
    };
}
